<template>
  <div class="">
    <h5>每日手写代码</h5>
    <input id="input" />
    <div id="span"></div>
  </div>
</template>

<script setup lang="ts">
  import { ref } from 'vue'

  // 执行上下文栈 先把 first加入执行栈，遇到second再加入执行栈，遵循后进先出的原则， 所以执行顺序 second() =》 first()
  let a = 'hello world!'
  function first() {
    console.log('inside first function')
    second()
    console.log('again inside first function')
  }
  function second() {
    console.log('inside second function')
  }
  first()

  // js 实现输出1-100，把能被3整除的数字替换成a，能被5整除的数字替换成b，既能被3又能被5整除的数替换成c
  function count1() {
    let str = ref<string>('')
    for (let i = 1; i <= 100; i++) {
      let value = ''
      if (i % 3 == 0 && i % 5 == 0) {
        value = 'c'
      } else if (i % 3 == 0) {
        value = 'a'
      } else if (i % 5 == 0) {
        value = 'b'
      } else {
        value = i.toString()
      }
      str.value += value + ','
    }
    console.log(str.value.split(',').slice(0, -1))
  }

  // ======================================= 数组去重 =======================================
  var arr = ['12', '32', '89', '12', '12', '78', '12', '32']
  function deWeight1() {
    let result = []
    for (let i = 0; i < arr.length; i++) {
      if (result.indexOf(arr[i]) === -1) {
        result.push(arr[i])
      }
    }
    console.log(result, 'result1')
  }

  function deWeight2() {
    console.log([...new Set(arr)])
  }

  function deWeight3() {
    const result = arr.filter((item, index, self) => {
      return self.indexOf(item) === index
    })
    console.log(result, 'result3')
  }

  function deWeight4() {
    let result = arr.reduce((prev, cur) => {
      if (!prev.includes(cur)) {
        prev.push(cur)
      }
      return prev
    }, [])
    console.log(result, 'result4')
  }

  // ================== map 是键值对形式，键和值可以是任何值，多次对同一个键set时，会覆盖之前的值 ==================
  function deWeight5() {
    let map = new Map()
    let result: string[] = []
    arr.forEach(item => {
      if (!map.has(item)) {
        map.set(item, true)
        result.push(item)
      }
    })
    console.log(result, 'result5')
  }
  count1()
  deWeight1()
  deWeight2()
  deWeight3()
  deWeight4()
  deWeight5()

  // instanceof 考察对原型链的理解，核心: 原型链的向上查找。  instance实例对象 conFun构造函数
  // 原理实例._proto_ === 构造函数.prototype
  function _instanceof(instance: any, constructor: any) {
    if (typeof instance !== 'object' || instance == null) return false
    let proto = Object.getPrototypeOf(instance) //相当于 instance._proto_; 如果该对象没有原型，则返回 null。
    console.log(proto, 'proto')
    while (proto !== null) {
      if (proto === constructor.prototype) return true
      proto = Object.getPrototypeOf(proto)
    }
    return false // 查找到尽头，还没找到
  }

  console.log('test', _instanceof([1, 2], Array))
  console.log('test', _instanceof({}, Array))
  console.log(_instanceof(null, Array))
  console.log(_instanceof('', Array))

  // ======================================= 手写 new 操作符 =======================================

  function myNew(constructor: Function, ...args: any) {
    const obj = Object.create(constructor.prototype)
    const result = constructor.apply(obj, args)
    return result instanceof Object ? result : obj
  }

  let myName = ''
  function Person(name: string) {
    myName = name
  }
  const preson1 = myNew(Person, 'Alice')
  console.log(preson1, 'new preson1')

  // ======================================= 实现一个sleep函数 =======================================
  function sleep(time: number) {
    return new Promise(resolve => {
      setTimeout(resolve, time)
    })
  }
  async function timeout() {
    console.log('time start')
    await sleep(2000)
    console.log('time end')
  }
  timeout()

  //   实现每隔1s打印1,2,3,4
  function consoleNum() {
    // let 块级作用域实现
    // for (let i = 1; i < 5; i++) {
    //   setTimeout(() => {
    //     console.log(i)
    //   }, 1000 * i)
    // }
    // 闭包实现 立即调用函数表达式
    for (let i = 1; i < 5; i++) {
      ;(function (j) {
        setTimeout(() => {
          console.log(j)
        }, j * 1000)
      })(i)
    }
  }

  consoleNum()

  //  实现 Object.defineProperty 监听数组
  // Object.defineProperty 监听的是属性级别的
  let proxyArr = ['1', '2', '3']
  function proxyfun(arr: string[]) {
    arr.forEach((val, idx) => {
      Object.defineProperty(arr, idx, {
        get() {
          console.log(`获取数组 ${idx} 元素的值：${val}`)
          return val
        },
        set(newVal) {
          val = newVal
          console.log(`修改数组 ${idx} 元素的值：${newVal}`)
        }
      })
    })
  }

  proxyfun(proxyArr)
  proxyArr[0] = '999'
  console.log(proxyArr[0])

  // ======================================= 提取高度嵌套的对象里的指定属性 =======================================

  function extractFun() {
    const school = {
      classes: {
        stu: {
          name: '高度嵌套的铁锤',
          age: 18
        }
      }
    }
    const {
      classes: {
        stu: { name }
      }
    } = school
    console.log(name)
  }

  extractFun()

  // ======================================= 分割线 =======================================
  // Id相同的value相加
  let originData1 = [
    { id: '1', value: 10 },
    { id: '1', value: 20 },
    { id: '2', value: 10 }
  ]
  function sameReduce(arr: any[]) {
    let result = arr.reduce((acc, cur) => {
      let found = acc.find((item: any) => {
        return item.id === cur.id
      })
      if (found) {
        found.value += cur.value
      } else {
        acc.push({ id: cur.id, value: cur.value })
      }
      return acc
    }, [])
    return result
  }
  console.log(sameReduce(originData1), 'Id相同的value相加')

  // name相同的项，合并成一个数组对象
  const originData2 = [
    { name: 'tony', id: '1', age: '20' },
    { name: 'jack', id: '2', age: '21' },
    { name: 'tony', id: '3', age: '50' },
    { name: 'jack', id: '4', age: '10' },
    { name: 'mark', id: '5', age: '22' },
    { name: 'mark', id: '6', age: '40' }
  ]
  // 方法2
  function sameArr(arr: any[]) {
    let result = Object.values(
      arr.reduce((acc, cur) => {
        if (acc[cur.name]) {
          acc[cur.name].origin.push(cur)
        } else {
          acc[cur.name] = { name: cur.name, origin: [cur] }
        }
        return acc
      }, {})
    )
    return result
  }
  // 方法1
  // function sameArr(arr: any[]) {
  //   let name = [] as any[] //存放相同name
  //   let result = []
  //   for (let i = 0; i < arr.length; i++) {
  //     if (name.includes(arr[i].name)) {
  //       for (let j = 0; j < result.length; j++) {
  //         if (result[j].name === arr[i].name) {
  //           result[j].origin.push(arr[i])
  //         }
  //       }
  //     } else {
  //       name.push(arr[i].name)
  //       result.push({ name: arr[i].name, origin: [arr[i]] })
  //     }
  //   }
  //   return result
  // }
  console.log(sameArr(originData2), 'sameArr')

  // ======================================= 手写迭代器 Iterator 解决 forEach 中用 await 处理异步不按顺序执行的问题 =======================================
  // 首先介绍下迭代器写法
  let iteratorArr = [4, 2, 1]
  // 这就是迭代器
  let iterator = iteratorArr[Symbol.iterator]()
  console.log(iterator.next())
  console.log(iterator.next())
  console.log(iterator.next())
  console.log(iterator.next())

  // 使用迭代器解决forEach处理异步不按顺序执行问题
  // async function testIterator1() {
  //   let arr = [4, 2, 1]
  //   let iterator = arr[Symbol.iterator]()
  //   let res = iterator.next()

  //   while (!res.done) {
  //     let value = res.value
  //     console.log(value, 'iterator')
  //     await handleArrTest(value)
  //     res = iterator.next()
  //   }
  //   console.log('结束')
  // }

  // testIterator1()
  function handleArrTest(x: number) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(x)
      }, 1000 * x)
    })
  }

  // 上面是for...of的语法糖，下面直接用for...of实现
  async function testIterator2() {
    let arr = [4, 2, 1]
    for (const item of arr) {
      const res = await handleArrTest(item)
      console.log(res)
    }
    console.log('结束')
  }
  testIterator2()
</script>

<style lang="less" scoped></style>
